/**************************************************************************************************
T R E E B O X E S
**************************************************************************************************/

// Tree box parameters
var treeboxes = new Array();

// Create treebox
treeboxes["exampletb"] = new Object();

// Display mode - either tree or list
treeboxes["exampletb"].display_mode = "tree";

// Current order to sort list mode in
treeboxes["exampletb"].sort_attribute = "order";

// Currently selected item
treeboxes["exampletb"].selected_item = false;

// Current path to selected item
treeboxes["exampletb"].selected_item_path = false;

// Current 'change parent' mode - either on (true) or off (false)
treeboxes["exampletb"].change_parent_mode = false;

// Current XML
treeboxes["exampletb"].xml = false;

// Current XML string - used for refreshing
treeboxes["exampletb"].xml_str = "";

// Tree Output - for generating the HTML tree
treeboxes["exampletb"].tree_output = "";

// Icons
treeboxes["exampletb"].folder_icon = false;
treeboxes["exampletb"].file_icon = false;

// Search value and type
treeboxes["exampletb"].search_value = "";
treeboxes["exampletb"].search_type = "all";

/**************************************************************************************************
V A R I A B L E S
**************************************************************************************************/


/**************************************************************************************************
S E A R C H   F U N C T I O N S
**************************************************************************************************/

/**
 * doSearch() executes a search
 *
 * @param tree_box is the tree box
 * @param value is the value to search for
 * @param type is either 'all' or 'any'
*/
function doSearch(tree_box, value, type) {
	
	// Check value
	value = value || value == "" ? value : treeboxes[tree_box].search_value;
	
	// Check search type
	type = type ? type : treeboxes[tree_box].search_type;
	
	// Assign values to globals
	treeboxes[tree_box].search_value = value;
	treeboxes[tree_box].search_type = type;
	
	// Check we're in list mode and sorting by name
	if (treeboxes[tree_box].display_mode != "list") {
		convertTreeToList(tree_box, "doSearch(tree_box);");
		return false;
	} else {
		if (treeboxes[tree_box].sort_attribute != "name") {
			sortListXML("name", tree_box, "doSearch(tree_box);");
			return false;
		}
	}
	
	// If no search, reset
	if (treeboxes[tree_box].search_value == "") {	
		endSearch(tree_box);
		return false;
	}
	
	// OK, press on
	var search_split = treeboxes[tree_box].search_value.toLowerCase().split(" ");
	
	// All items inside list
	var items = getItemNodesIn(treeboxes[tree_box].xml.getElementsByTagName("list")[0]);
	
	// For each
	//for (var i in items) {
	for (var i = 0; i < items.length; i++) {
		
		// Get <li>
		var li = document.getElementById(tree_box + items[i].getAttribute("id"));
		
		// If set to be hidden on search, ditch it now
		if (items[i].getAttribute("nosearch") == "1") {
			
			// Add Hidden class
			addClass(li, "hidden");
			
		} else {
			
			// Value to compare to search
			var item_value = "" + items[i].getAttribute("name");
			
			// Compare
			if (compareSearch(item_value, search_split, type == "any")) {
				removeClass(li, "hidden");
			} else {
				addClass(li, "hidden");
			}
		}
	}
}


/**
 * compareSearch() does a search on a passed value. Search can be an any or an all search
 *
 * @param value is the value to look at
 * @param search_terms is an array of search terms
 * @param any_search is a var indicating to do a 'any' search. Defaults to false
 *
 * @return true if a match is found, otherwise false
*/
function compareSearch(value, search_terms, any_search) {
	
	// If has to match all
	if (!any_search) {
		
		// For each search term
		//for (var i in search_terms) {
		for (var i = 0; i < search_terms.length; i++) {
			
			// If search term exists
			if (search_terms[i] != "") {
			
				// If fails to match, return a failure
				if (value.toLowerCase().indexOf(search_terms[i].toLowerCase()) == -1) {
					return false;
				}
			}
		}
		
		// Otherwise it's good
		return true;
		
	} else {
		
		// For each search term
		//for (var i in search_terms) {
		for (var i = 0; i < search_terms.length; i++) {
			
			// If search term exists
			if (search_terms[i] != "") {
			
				// If it matches, return success
				if (value.toLowerCase().indexOf(search_terms[i].toLowerCase()) != -1) {
					return true;
				}
			}
		}
		
		// Otherwise it fails
		return false;
	}
}


/**
 * endSearch() ends a search
 *
 * @param tree_box is the tree box
*/
function endSearch(tree_box) {
	
	// Search value
	search_value = "";
	
	// Set textbox
	try {
		document.getElementById("search").value = "";
	} catch (error) { }
	
	// Reset any/all
	search_type = "all";
	
	// Set radio buttons
	try {
		document.getElementById("searchany").checked = false;
		document.getElementById("searchall").checked = true;
	} catch (error) { }
	
	// All items inside list
	var items = getItemNodesIn(treeboxes[tree_box].xml.getElementsByTagName("list")[0]);
	
	// For each
	//for (var i in items) {
	for (var i = 0; i < items.length; i++) {
		
		// Get LI
		var li = document.getElementById(tree_box + items[i].getAttribute("id"));
		
		// add hiding class
		removeClass(li, "hidden");
	}
}


/**************************************************************************************************
X M L   S E L E C T I N G   F U N C T I O N S
**************************************************************************************************/

/**
 * selectItem() selects an item in the tree box
 *
 * @param item_id is the ID of the item
 * @param tree_box is the tree box
*/
function selectItem(item_id, tree_box) {
	
	// Check if selecting a list
	if (item_id == "list") {
	
		// Must be in page move mode
		if (treeboxes[tree_box].change_parent_mode != false && treeboxes[tree_box].selected_item != false) {
			
			// Get node to move
			var move_node = getItemNode(treeboxes[tree_box].selected_item, tree_box);
			
			// Get parent node
			var parent_node = treeboxes[tree_box].xml.getElementsByTagName("list")[0];
		
			// If OK
			if (move_node && parent_node) {
			
				// Clone
				var clone_node = move_node.cloneNode(true);
			
				// Remove orig
				move_node.parentNode.removeChild(move_node);
			
				// Move
				parent_node.appendChild(clone_node);
			}
		
			// Turn mode off
			setChangeParentMode(false, tree_box);
		
			// Refresh XML
			treeboxes[tree_box].xml = refreshXML(treeboxes[tree_box].xml, tree_box);
		
			// Redraw
			printTreeXML(tree_box);
		
			// Select parent item properly now
			selectItem(treeboxes[tree_box].selected_item, tree_box);
		}
		
	} else {
	
		// Check move mode
		if (treeboxes[tree_box].change_parent_mode == false) {
	
			// See if previous item <li> is on
			if (document.getElementById(tree_box + treeboxes[tree_box].selected_item)) {
			
				// Get <a> item with title
				var a_tags = document.getElementById(tree_box + treeboxes[tree_box].selected_item).getElementsByTagName("a");
				//for (var i in a_tags) {
				for (var i = 0; i < a_tags.length; i++) {
					if (hasClass(a_tags[i], "title")) {
						removeClass(a_tags[i], "selected");
						break;
					}
				}
			}
	
			// Get <a> item with title, add selected
			var a_tags = document.getElementById(tree_box + item_id).getElementsByTagName("a");
			//for (var i in a_tags) {
			for (var i = 0; i < a_tags.length; i++) {
				if (hasClass(a_tags[i], "title")) {
					addClass(a_tags[i], "selected");
					break;
				}
			}
	
			// Set global
			treeboxes[tree_box].selected_item = item_id;
		
		} else {
		
			// Try to move here
			var move_item = treeboxes[tree_box].selected_item;
		
			// Get parent node to move to
			var parent_node = getItemNode(item_id, tree_box);
		
			// See if they are the same
			if (move_item == item_id) {
				alert("Can't make an item it's own parent!");
			} else {
			
				// Get node to move
				var move_node = getItemNode(move_item, tree_box);
			
				// If OK
				if (move_node && parent_node) {
				
					// Clone
					var clone_node = move_node.cloneNode(true);
				
					// Remove orig
					move_node.parentNode.removeChild(move_node);
				
					// Move
					parent_node.appendChild(clone_node);
				}
			}
		
			// Turn mode off
			setChangeParentMode(false, tree_box);
		
			// Refresh XML
			treeboxes[tree_box].xml = refreshXML(treeboxes[tree_box].xml, tree_box);
		
			// Redraw
			printTreeXML(tree_box);
		
			// Select parent item properly now
			selectItem(parent_node.getAttribute("id"), tree_box);
		}
	}
}


/**************************************************************************************************
X M L   M O V I N G   F U N C T I O N S
**************************************************************************************************/

/**
 * toggleChangeParentMode() toggles the change parent mode on or off
 *
 * @param tree_box is the tree box
*/
function toggleChangeParentMode(tree_box) {
	
	// Change
	setChangeParentMode(!treeboxes[tree_box].change_parent_mode, tree_box);
}


/**
 * setChangeParentMode() sets the change parent mode on or off
 *
 * @param mode_on is whether the mode is to be on or not
 * @param tree_box is the tree box
*/
function setChangeParentMode(mode_on, tree_box) {
	
	// Change button
	if (mode_on) {
		
		// If turning on, require a selected item
		if (treeboxes[tree_box].selected_item) {
			document.getElementById("changeparent").value = "Select a Parent...";
			treeboxes[tree_box].change_parent_mode = mode_on;
		}
	} else {
		document.getElementById("changeparent").value = "Change Item Parent";
		treeboxes[tree_box].change_parent_mode = mode_on;
	}
}

/**
 * getItemNode() gets an item node from the XML
 *
 * @param item_id is the Item ID to get the node of
 * @param tree_box is the tree box
 *
 * @return the item node
*/
function getItemNode(item_id, tree_box) {
	
	// Get all item nodes
	var items = getItemNodesIn(treeboxes[tree_box].xml);
	
	//for (var i in items) {
	for (var i = 0; i < items.length; i++) {
		if (items[i].getAttribute("id") == item_id) {
			return items[i];
		}
	}
	
	// Fail
	return false;
}


/**
 * getItemNodesIn() gets all <item> nodes in the passed node
	To get only items directly under this node, use getItemChildrenOf()
 *
 * @param node is the node to get the items in
 *
 * @return an array of nodes
*/
function getItemNodesIn(node) {
	
	// Array
	var arr = new Array();
	
	// Use getElementsByTagName() first
	var items = node.getElementsByTagName("item");
	
	// For each
	//for (var i in items) {
	for (var i = 0; i < items.length; i++) {
		try {
			if (items[i] && items[i].nodeType == 1 && items[i].tagName.toLowerCase() == "item") {
				arr.push(items[i]);
			}
		} catch (error) {}
	}
	
	// Return
	return arr;
}


/**
 * getItemChildrenOf() gets the children <item> nodes of a passed node
	To get all items under a passed node, use getItemNodesIn()
 *
 * @param node is the node to get items in
 *
 * @return an array of nodes
*/
function getItemChildrenOf(node) {
	
	// Array
	var arr = new Array();
	
	// Child nodes
	var items = node.childNodes;
	
	// For each (again, Safari needs a full for loop...)
	for (var i = 0; i < items.length; i++) {
		try {
			if (items[i] && items[i].nodeType == 1 && items[i].tagName.toLowerCase() == "item") {
				arr.push(items[i]);
			}
		} catch (error) {}
	}
	
	// Return
	return arr;
}


/**
 * getItemPath() gets the items in the path from the <list> to the passed item ID
 *
 * @param item_id is the item to get the path to
 * @param tree_box is the tree box
 *
 * @return an array of item IDs to get to the passed item
*/
function getItemPath(item_id, tree_box) {
	
	// Get Item
	var item = getItemNode(item_id, tree_box);
	
	// Path
	var path = new Array();
	
	// If it succeeded
	if (item) {
		while (item.parentNode.tagName.toLowerCase() == "item") {
			path.push(item.parentNode.getAttribute("id"));
			item = item.parentNode;
		}
	}
	
	// Return
	return path;
}


/**
 * moveItem() moves the selected node up or down
 *
 * @param direction is the direction to move the mode - up or down
 * @param tree_box is the tree box
*/
function moveItem(direction, tree_box) {

	// Check item is selected
	if (treeboxes[tree_box].selected_item) {
	
		// Check direction is either "down" or "up"
		direction = direction == "down" ? direction : "up";
		
		// Must be in Tree mode
		if (treeboxes[tree_box].display_mode != "tree") {
			
			// If not in tree mode, convert, then return here
			convertListToTree(tree_box, "moveItem('" + direction + "', tree_box);")
			return false;
		}
		
		// Identify the node
		var item = getItemNode(treeboxes[tree_box].selected_item, tree_box);

		// Get parent
		var parent_node = item.parentNode.tagName == "item" ? item.parentNode.getAttribute("id") : false;
		
		// If item OK
		if (item) {
			
			// Sibling to replace
			var replace_sibling = false;
			
			// If going down
			if (direction == "down") {
				
				// Check it can go down
				for (var sibling = item.nextSibling; sibling; sibling = sibling.nextSibling) {
					if (sibling.nodeType == 1) {
						replace_sibling = sibling;
						break;
					}
				}
				
			// If going down
			} else {
				
				// Check it can go up
				for (var sibling = item.previousSibling; sibling; sibling = sibling.previousSibling) {
					if (sibling.nodeType == 1) {
						replace_sibling = sibling;
						break;
					}
				}
			}
			
			// If replacement found
			if (replace_sibling) {
				
				// Swap nodes
				if (direction == "up") {
					swapNodes(item, replace_sibling);
				} else {
					swapNodes(replace_sibling, item);
				}
				
				// Refresh XML
				treeboxes[tree_box].xml = refreshXML(treeboxes[tree_box].xml, tree_box);
				
				// Update
				printTreeXML(tree_box, parent_node);

			} else {
				// alert("That move is so invalid it needs a wheelchair");
			}
			
		} else {
			alert("Couldn't find Item. Have you selected one yet?")
		}
	} else {
	}
}


/**
 * swapNodes swaps the location of two nodes. Node 1 MUST be on top of node 2
 *
 * @param node1 is the first node
 * @param node2 is the second node
*/
function swapNodes(node1, node2) {
	
	// Create clone
	var clone_node = node1.cloneNode(true);
	
	// Insert clone before node2
	node2.parentNode.insertBefore(clone_node, node2);
	
	// Delete original
	node1.parentNode.removeChild(node1);
}


/**************************************************************************************************
X M L   L I S T   S O R T I N G   F U N C T I O N S
**************************************************************************************************/

/**
 * toggleListSort() toggles sorting the List XML by an attribute. If it's on, the list is resorted
	alphabetically by the value of the passed attribute, otherwise it is sorted numerically by order
 *
 * @param cb_id is the Checkbox that was clicked to trigger this function
 * @param sort_attribute is the attribute to sort by
 * @param tree_box is the tree box
*/
function toggleListSort(checked, sort_attribute, tree_box) {
	
	// Make sure we're in List mode
	if (treeboxes[tree_box].display_mode != "list") {
		convertTreeToList(tree_box);
	}
	
	// If checked, sort by sort attribute, otherwise sort by order
	if (checked) {
		sortListXML(sort_attribute, tree_box)
	} else {
		sortListXML("order", tree_box);
	}
}


/**
 * sortListXML() sorts the list XML. If the sort_attribute is 'order', it is sorted numerically by the
	order attribute, otherwise it is sorted alphabetically by the passed attribute
 *
 * @param sort_attribute is the attribute to sort on
 * @param tree_box is the tree box
 * @param eval_statement is the statement to eval at the end (optional)
*/
function sortListXML(sort_attribute, tree_box, eval_statement) {
	
	// Check sort_attribute
	sort_attribute = sort_attribute ? sort_attribute : "order";
	
	// Check XML object
	var xml_obj = treeboxes[tree_box].xml;
	
	// Create array to store XML items in
	var item_array = new Array();
	
	// Create array to store rejects that don't have the attribute in
	var reject_array = new Array();
	
	// Get items
	var items = getItemNodesIn(xml_obj);
	
	// For each item
	//for (var i in items) {
	for (var i = 0; i < items.length; i++) {
				
		// Looks like we'll have to brute force search it
		
		// If attribute exists for this element
		if (items[i].getAttribute(sort_attribute)) {
			
			// Value to compare
			var value = items[i].getAttribute(sort_attribute);
		
			// If it's a number, try and convert it
			value = Number(value) ? Number(value) : value;
		
			// Search the item array looking for a place to slot it in
			
			// Has it been inserted yet?
			var inserted = false;
		
			// For each item in the array so far
			//for (var j in item_array) {
			for (var j = 0; j < item_array.length; j++) {
			
				// If it's smaller (lower, before, ...) the current one, splice it in
				if (value < item_array[j][0]) {
					item_array.splice(j, 0, new Array(value, items[i]));
					inserted = true;
					break;
				}
			}
		
			// If not inserted in the array yet, put at end
			if (!inserted) {
				//item_array.push(new Array(value, items[i]));
				item_array.push(new Array(value, items[i].cloneNode(true)));
			}
		
		// If attribute doesn't exist, put item in reject array
		} else {
			reject_array.push(new Array("", items[i].cloneNode(true)))
		}
	}

	// Create <list> element
	var list = document.createElement("list");
	
	// Merge reject and main array
	item_array = reject_array.concat(item_array);
	
	// Add items from array in
	for (var k = 0; k < item_array.length; k++) {

		// Create item
		var item = document.createElement("item");

		// Copy the attributes
		var attr = getAttributes(item_array[k][1]);
		for (var m in attr) {
			item.setAttribute(m, attr[m]);
		}

		// Append
		list.appendChild(item);
	}

	// Create XML for the container
	var sorted_xml = "<?xml version=\"1.0\"?><list";

	// Attributes
	var attr = getAttributes(xml_obj.getElementsByTagName("list")[0]);
	for (var i in attr) {
		//if (findInArray(invalid_xml_attr, i) == -1) {
			sorted_xml += " " + i + "=\"" + attr[i] + "\"";
		//}
	}

	// Close List
	sorted_xml += ">" + list.innerHTML.replace(/id\=([0-9]+)/gi, "id=\"$1\"") + "</list>";
	
	// Update XML
	treeboxes[tree_box].xml = refreshXML(stringToXML(sorted_xml), tree_box);
	
	// Update global
	treeboxes[tree_box].sort_attribute = sort_attribute;
	
	// Redraw
	printTreeXML(tree_box);
	
	// If eval, do now
	if (eval_statement) {
		eval(eval_statement);
	}
}


/**************************************************************************************************
X M L   P R I N T   F U N C T I O N S
**************************************************************************************************/

/**
 * printTreeXML() prints an XML tree list. Contrary to it's name, it also prints List XML (as
	List XML is just a flat tree)
 *
 * @param tree_box is the tree box
 * @param node_id is the Node ID to redraw (if only redrawing one section). Otherwise redraws all
*/
function printTreeXML(tree_box, node_id) {

	// Check node id
	node_id = node_id ? node_id : false;
	
	// Get XML
	var xml_obj = treeboxes[tree_box].xml;
	
	// Output
	printXML(xml_obj);

	// Create containing UL object
	treeboxes[tree_box].print_xml = document.createElement("ul");

	// If no node, redaw everything
	if (!node_id) {

		// Get <list> item
		var list = xml_obj.getElementsByTagName("list")[0];

		// Set folder/file icon
		if (list.getAttribute("fileicon")) {
			treeboxes[tree_box].file_icon = list.getAttribute("fileicon");
			treeboxes[tree_box].folder_icon = list.getAttribute("foldericon");
		}

		// Get children
		var children = getItemChildrenOf(list);
		for (var i = 0; i < children.length; i++) {
			printTreeItem(children[i], tree_box, treeboxes[tree_box].print_xml);
		}

		// Compile elements - the UL, the H2, and the Message (H3)
		var ul = "<ul>" + treeboxes[tree_box].print_xml.innerHTML + "</ul>";

		// Header
		if (list.getAttribute("show") == "1" && list.getAttribute("name")) {

			// Open
			var h2 = "<h2 onclick=\"selectItem('list', '" + tree_box + "');\">";

			// Image?
			if (list.getAttribute("listicon")) {
				h2 += "<img class=\"iconimg\" src=\"../assets/images/" + list.getAttribute("listicon") + "\" alt=\"Icon\" />";
			}

			// Name
			h2 += list.getAttribute("name") + "</h2>";
		} else {
			var h2 = "";
		}

		// Message
		var h3 = "<h3>" + (list.getAttribute("message") ? list.getAttribute("message") : "Viewing in " + treeboxes[tree_box].display_mode + " mode") + "</h3>";

		// Set HTML
		document.getElementById(tree_box).innerHTML = h2 + h3 + ul;

	} else {

		// Get node
		var node = getItemNode(node_id, tree_box);

		// Get children
		var children = getItemChildrenOf(node);
		for (var i = 0; i < children.length; i++) {
			printTreeItem(children[i], tree_box, treeboxes[tree_box].print_xml);
		}

		// OK, now output is ready, have to identify the <ul> it goes into
		var ul = document.getElementById(tree_box + node_id).getElementsByTagName("ul")[0];

		// Set Inner
		ul.innerHTML = treeboxes[tree_box].print_xml.innerHTML;

	}
}


/**
 * printTreeItem() prints an <item> tag in the Tree (or List) XML.
 *
 * @param xml_node is the XML <item> node to print
 * @param tree_box is the tree box
 * @param parent is the node to put this li in
*/
function printTreeItem(xml_node, tree_box, parent) {

	// If in list mode and no list is set, don't show
	if (treeboxes[tree_box].display_mode == "list" && xml_node.getAttribute("nolist") == "1") {
		
	} else {

		// Get children of node
		var children = getItemChildrenOf(xml_node);

		// Name and ID
		var item_name = xml_node.getAttribute("name");
		var item_id = xml_node.getAttribute("id");

		// Check expanded exists
		if (!xml_node.getAttribute("expanded")) {
		
			// Is it in path of selected item?
			if (findInArray(treeboxes[tree_box].selected_item_path, item_id) != -1) {
				xml_node.setAttribute("expanded", "1");
			}  else {
				xml_node.setAttribute("expanded", "0");
			}
		
			// If no selected item, expand all
			if (!treeboxes[tree_box].selected_item) {
				xml_node.setAttribute("expanded", "1");
			}
		}

		// Get expanded value
		var expanded = xml_node.getAttribute("expanded") == "1";
		
		// Create list item
		var li = document.createElement("li");

		// Assign class and ID
		li.setAttribute("class", (expanded ? "open" : "closed"));
		li.setAttribute("id", tree_box + xml_node.getAttribute("id"));

		// Title holder
		var titleholder = document.createElement("div");
		titleholder.setAttribute("class", "titleholder");

		// Expander
		var expander = document.createElement("a");
		expander.setAttribute("href", "#");
		if (children.length > 0) {
			expander.setAttribute("onclick", "toggleTreeExpansion(" + item_id + ", '" + tree_box + "');");
			expander.setAttribute("class", "expander");
		} else {
			expander.setAttribute("class", "noexpander");
		}
		expander.innerHTML = "&nbsp;";

		// Title
		var title = document.createElement("a");
		title.setAttribute("class", (treeboxes[tree_box].selected_item == xml_node.getAttribute("id") ? "title selected" : "title"));
		title.setAttribute("href", "#");
		title.setAttribute("onclick", "selectItem(" + item_id + ", '" + tree_box + "');");
		title.innerHTML = "<img src=\"../assets/images/" + (children.length > 0 ? treeboxes[tree_box].folder_icon : treeboxes[tree_box].file_icon) + "\" class=\"iconimg\" alt=\"Icon\" />" + item_name;
	
		// Children
		if (children.length > 0) {

			// Create elements
			var children_div = document.createElement("div");
			children_div.setAttribute("class", "children");
			var children_ul = document.createElement("ul");

			// Add children
			for (var i = 0; i < children.length; i++) {
				printTreeItem(children[i], tree_box, children_ul);
			}
		}

		// Attach items: titleholder -> li, expander -> titleholder, li -> main ul
		titleholder.appendChild(expander);
		titleholder.appendChild(title);
		li.appendChild(titleholder);

		// If children exist, append them too
		if (children.length > 0) {
			children_div.appendChild(children_ul);
			li.appendChild(children_div);
		}

		// Attach entire LI to the parent UL
		parent.appendChild(li);

	}
}


/**
 * toggleTreeExpansion toggles the expansion of a branch of the tree
 *
 * @param item_id is the ID of the <item>
 * @param tree_box is the tree box
*/
function toggleTreeExpansion(item_id, tree_box) {
	
	// Set element
	var item = getItemNode(item_id, tree_box);
	var li = document.getElementById(tree_box + item_id);
	if (item) {
		if (item.getAttribute("expanded") == "1") {
			item.setAttribute("expanded", "0");
			removeClass(li, "open");
			addClass(li, "closed");
		} else {
			item.setAttribute("expanded", "1");
			removeClass(li, "closed");
			addClass(li, "open");
		}
	}
	
	// No need to reprint XML here, as it is wasteful. Just update class.
	//printTreeXML(tree_box, item_id);
	//alert(li.className);
}


/**************************************************************************************************
X M L   T R E E / L I S T   C O N V E R S I O N   F U N C T I O N S
**************************************************************************************************/

/**
 * convertTreeToList() converts a multilevel Tree XML to a flat List XML.
 *
 * @param tree box is the tree box
 * @param eval_statement (optional) is a statement to eval at end - usually a function to run
*/
function convertTreeToList(tree_box, eval_statement) {
	
	// Get XML
	var xml_obj = refreshXML(treeboxes[tree_box].xml, tree_box);
	
	// If XML exists
	if (xml_obj) {

		// Update Tree Box
		treeboxes[tree_box].xml = getXMLAsList(tree_box);

		// Mode
		treeboxes[tree_box].display_mode = "list";
		
		// Print to the HTML
		printTreeXML(tree_box);
		
		// Update buttons
		updateButtons(tree_box);
		
		// If a eval statement was passed, do it now
		if (eval_statement) {
			eval(eval_statement);
		}
	}
}


/**
 * getXMLAsList() gets the XML in the passed tree box in list form
 *
 * @param tree_box is the tree box
 * @param suppress_refresh cancels the initial refresh
*/
function getXMLAsList(tree_box, suppress_refresh) {

	// Get XML
	if (suppress_refresh) {
		var xml_obj = treeboxes[tree_box].xml;
	} else {
		var xml_obj = refreshXML(treeboxes[tree_box].xml, tree_box);
	}
	
	// If XML exists
	if (xml_obj) {

		// Create <list> tag
		var list_list = document.createElement("list");

		// Get <item> elements from Tree XML
		var tree_list = getItemNodesIn(xml_obj);
		
		// Keep count so we can assign a default order
		var counter = 0;
	
		// For each <item>
		for (var i = 0; i < tree_list.length; i++) {
					
			// Increment counter
			counter++;

			// Create item
			var item = document.createElement("item");

			// Set attributes
			for (var j = 0; j < tree_list[i].attributes.length; j++) {
				if ("" + tree_list[i].attributes[j].value != "undefined") {
					//if (findInArray(invalid_xml_attr, tree_list[i].attributes[j].name) == -1) {
						item.setAttribute(tree_list[i].attributes[j].name, tree_list[i].attributes[j].value);
					//}
				}
			}
		
			// Set parent
			if (tree_list[i].parentNode.tagName.toLowerCase() == "item") {
				item.setAttribute("parent", tree_list[i].parentNode.getAttribute("id"));
			}
			
			// Set order
			item.setAttribute("order", counter);

			// Attach
			list_list.appendChild(item);
		}


		// Create XML for the container
		var list_xml = "<?xml version=\"1.0\"?><list";

		// Attributes
		var attr = getAttributes(xml_obj.getElementsByTagName("list")[0]);
		for (var i in attr) {
			//if (findInArray(invalid_xml_attr, i) == -1) {
				list_xml += " " + i + "=\"" + attr[i] + "\"";
			//}
		}

		// Close List
		list_xml += ">" + list_list.innerHTML.replace(/id\=([0-9]+)/gi, "id=\"$1\"") + "</list>";
		
		// Return
		return refreshXML(stringToXML(list_xml), tree_box);
	} else {
		return false;
	}
}


/**
 * convertListToTree() converts a flat List XML to a Tree XML. The result is stored
	in the tree_xml global
 *
 * @param tree_box is the tree box
 * @param eval_statement (optional) is a statement to eval at end - usually a function to run
*/
function convertListToTree(tree_box, eval_statement) {
	
	// End any search
	endSearch(tree_box);
	
	// Get XML
	var xml_obj = refreshXML(treeboxes[tree_box].xml, tree_box);
	
	// If XML exists
	if (xml_obj) {
		
		// Ensure it is sorted by order - if not, do that first, then come back
		if (treeboxes[tree_box].display_mode == "list" && treeboxes[tree_box].sort_attribute != "order") {
			sortListXML("order", tree_box, "convertListToTree(tree_box);");
			return false;
		}

		// Create <list> tag
		var tree_list = document.createElement("list");
	
		// Get <item> elements from List XML
		var list_items = getItemNodesIn(xml_obj);
	
		// For each <item>
		//for (var i in list_items) {
		for (var i = 0; i < list_items.length; i++) {
	
			// Create Item
			var item = document.createElement("item");

			// Set attributes (except parent & order)
			//for (var j in list_items[i].attributes) {
			for (var j = 0; j < list_items[i].attributes.length; j++) {
				//if (findInArray(invalid_xml_attr, list_items[i].attributes[j].name) == -1) {
					if ("" + list_items[i].attributes[j].value != "undefined" && list_items[i].attributes[j].name.toLowerCase() != "parent" && list_items[i].attributes[j].name.toLowerCase() != "order") {
						item.setAttribute(list_items[i].attributes[j].name, list_items[i].attributes[j].value);
					}
				//}
			}
		
			// If node is a child of another <item>, found it now
			if (list_items[i].getAttribute("parent")) {
			
				// Get all current items in the Tree
				var all_tree_items = getItemNodesIn(tree_list);
				//for (var j in all_tree_items) {
				for (var j = 0; j < all_tree_items.length; j++) {
					
					// And if ID matches parent
					if (all_tree_items[j].getAttribute("id") == list_items[i].getAttribute("parent")) {
					
						// Append
						all_tree_items[j].appendChild(item);
					}
				}
		
			// ... Otherwise ...
			} else {
		
				// Append new Item at top level
				tree_list.appendChild(item);
			}
		}

		// Create XML for the container
		var tree_xml = "<?xml version=\"1.0\"?><list";

		// Attributes
		var attr = getAttributes(xml_obj.getElementsByTagName("list")[0]);
		for (var i in attr) {
			//if (findInArray(invalid_xml_attr, i) == -1) {
				tree_xml += " " + i + "=\"" + attr[i] + "\"";
			//}
		}

		// Close List
		tree_xml += ">" + tree_list.innerHTML.replace(/id\=([0-9]+)/gi, "id=\"$1\"") + "</list>";
		
		// Update global
		treeboxes[tree_box].xml = refreshXML(stringToXML(tree_xml), tree_box);
	
		// Mode
		treeboxes[tree_box].display_mode = "tree";
	
		// Print to the HTML
		printTreeXML(tree_box);
		
		// Set Button statuses
		updateButtons(tree_box);
		
		// If a eval statement was passed, do it now
		if (eval_statement) {
			eval(eval_statement);
		}
	}
}